var LLParser = require('./LLParser');
var RUNTIME_NAMES = require('./StringEscape').RUNTIME_NAMES;
var STRING_UNESCAPE_MAP = require('./StringEscape').STRING_UNESCAPE_MAP;
var STRING_ESCAPE_MAP = require('./StringEscape').STRING_ESCAPE_MAP;
var STRING_ESCAPE_REGEXP = require('./StringEscape').STRING_ESCAPE_REGEXP;
var l = null;
var d = ' \n\r\t\f';
var A = [
    'true',
    'false',
    'null',
    'undefined'
];
exports.parse = function (e) {
    l || (l = LLParser.create({
        VARIABLE: [{
                id: '_concat',
                states: [
                    'a-zA-Z_$',
                    'VARIABLE_AFTER'
                ]
            }],
        VARIABLE_AFTER: [
            {
                id: '_concat',
                states: [
                    'a-zA-Z_$0-9',
                    'VARIABLE_AFTER'
                ]
            },
            {
                id: '_null',
                states: ['NULL']
            }
        ],
        STRING: [
            {
                id: 'str^',
                states: [
                    '\'',
                    'STRING_INNER_1'
                ]
            },
            {
                id: 'str^',
                states: [
                    '"',
                    'STRING_INNER_2'
                ]
            }
        ],
        STRING_INNER_1: [
            {
                id: 'str$',
                states: ['\'']
            },
            {
                id: 'str\\',
                states: [
                    '\\',
                    'nrbftv\'"\\\r\n',
                    'STRING_INNER_1'
                ]
            },
            {
                id: '_concat',
                states: [
                    'ALL',
                    'STRING_INNER_1'
                ]
            }
        ],
        STRING_INNER_2: [
            {
                id: 'str$',
                states: ['"']
            },
            {
                id: 'str\\',
                states: [
                    '\\',
                    'nrbftv\'"\\\r\n',
                    'STRING_INNER_2'
                ]
            },
            {
                id: '_concat',
                states: [
                    'ALL',
                    'STRING_INNER_2'
                ]
            }
        ],
        NUMBER: [{
                id: '_concat',
                states: [
                    'NUMBER_BEFORE_E',
                    'NUMBER_E'
                ]
            }],
        NUMBER_E: [
            {
                id: '_concat',
                states: [
                    'e',
                    'NUMBER_AFTER_E'
                ]
            },
            {
                id: '_null',
                states: ['NULL']
            }
        ],
        NUMBER_AFTER_E: [
            {
                id: '_concat',
                states: [
                    '-',
                    'NUMBER_AFTER_E-INT'
                ]
            },
            {
                id: '_jump',
                states: ['NUMBER_AFTER_E-INT']
            }
        ],
        'NUMBER_AFTER_E-INT': [
            {
                id: '_concat',
                states: [
                    'NUMBER_AFTER_E-INT',
                    '0-9'
                ]
            },
            {
                id: '_jump',
                states: ['0-9']
            }
        ],
        NUMBER_BEFORE_E: [
            {
                id: '_concat',
                states: [
                    '.',
                    'NUMBER_AFTER_DOT'
                ]
            },
            {
                id: '_concat',
                states: [
                    '0',
                    'NUMBER_AFTER_ZERO'
                ]
            },
            {
                id: '_concat',
                states: [
                    '1-9',
                    'NUMBER_AFTER_NON_ZERO'
                ]
            }
        ],
        NUMBER_AFTER_ZERO: [
            {
                id: '_concat',
                states: [
                    'x',
                    'NUMBER_BASE_16'
                ]
            },
            {
                id: '_jump',
                states: ['NUMBER_AFTER_NON_ZERO']
            },
            {
                id: '_null',
                states: ['NULL'],
                overwrite: true
            }
        ],
        NUMBER_BASE_16: [{
                id: '_concat',
                states: [
                    '0-9a-fA-F',
                    'NUMBER_BASE_16-AFTER'
                ]
            }],
        'NUMBER_BASE_16-AFTER': [
            {
                id: '_concat',
                states: [
                    '0-9a-fA-F',
                    'NUMBER_BASE_16-AFTER'
                ]
            },
            {
                id: '_null',
                states: ['NULL'],
                overwrite: true
            }
        ],
        NUMBER_AFTER_NON_ZERO: [
            {
                id: '_concat',
                states: [
                    '.',
                    'NUMBER_AFTER_DOT'
                ]
            },
            {
                id: '_concat',
                states: [
                    '0-9',
                    'NUMBER_AFTER_NON_ZERO'
                ]
            },
            {
                id: '_null',
                states: ['NULL'],
                overwrite: true
            }
        ],
        NUMBER_AFTER_DOT: [
            {
                id: '_concat',
                states: [
                    'NUMBER_AFTER_DOT',
                    '0-9'
                ]
            },
            {
                id: '_jump',
                states: ['0-9']
            }
        ],
        VALUE: [
            {
                id: '_blank',
                states: [
                    d,
                    'VALUE'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['VALUE_NOSPACE']
            }
        ],
        VALUE_NOSPACE: [
            {
                id: '_jump',
                states: [
                    'VALUE_NOSPACE',
                    d
                ]
            },
            {
                id: 'var',
                states: ['VARIABLE']
            },
            {
                id: 'str',
                states: ['STRING']
            },
            {
                id: 'num',
                states: ['NUMBER']
            },
            {
                id: '()',
                states: [
                    '(',
                    'COMMA',
                    ')'
                ]
            },
            {
                id: 'al',
                states: [
                    '[',
                    'ARRAY_ITEMS',
                    ']'
                ]
            },
            {
                id: 'ol',
                states: [
                    '{',
                    'OBJECT_ITEMS',
                    '}'
                ]
            }
        ],
        ARRAY_ITEMS: [
            {
                id: '_blank',
                states: [
                    d,
                    'ARRAY_ITEMS'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['ARRAY_ITEMS_NOSPACE']
            }
        ],
        ARRAY_ITEMS_NOSPACE: [
            {
                id: '_join',
                states: [
                    'ARRAY_ITEMS_NOSPACE',
                    ',',
                    'ARRAY_ITEM'
                ]
            },
            {
                id: '_jump',
                states: ['ARRAY_ITEM']
            },
            {
                id: '_empty',
                states: ['NULL'],
                overwrite: true
            }
        ],
        ARRAY_ITEM: [
            {
                id: '_blank',
                states: [
                    d,
                    'ARRAY_ITEM'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['ARRAY_ITEM_NOSPACE']
            }
        ],
        ARRAY_ITEM_NOSPACE: [
            {
                id: 'ai',
                states: ['CONDITIONAL']
            },
            {
                id: 'aie',
                states: ['NULL'],
                overwrite: true
            }
        ],
        OBJECT_ITEMS: [
            {
                id: '_join',
                states: [
                    'OBJECT_ITEMS',
                    ',',
                    'OBJECT_ITEM_AFTER_COMMA'
                ]
            },
            {
                id: '_jump',
                states: ['OBJECT_ITEM']
            },
            {
                id: '_empty',
                states: ['NULL'],
                overwrite: true
            }
        ],
        OBJECT_ITEM_AFTER_COMMA: [
            {
                id: '_blank',
                states: [
                    d,
                    'OBJECT_ITEM_AFTER_COMMA'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['OBJECT_ITEM']
            },
            {
                id: '_empty',
                states: ['NULL'],
                overwrite: true
            }
        ],
        OBJECT_ITEM: [
            {
                id: '_blank',
                states: [
                    d,
                    'OBJECT_ITEM'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['OBJECT_ITEM_NOSPACE']
            }
        ],
        OBJECT_ITEM_NOSPACE: [
            {
                id: '_jump',
                states: [
                    'OBJECT_ITEM_NOSPACE',
                    d
                ]
            },
            {
                id: 'onv',
                states: [
                    'VARIABLE',
                    'OBJECT_ITEM_AFTER'
                ]
            },
            {
                id: 'ons',
                states: [
                    'STRING',
                    'OBJECT_ITEM_AFTER'
                ]
            },
            {
                id: 'onn',
                states: [
                    'NUMBER',
                    'OBJECT_ITEM_AFTER'
                ]
            }
        ],
        OBJECT_ITEM_AFTER: [
            {
                id: '_blank',
                states: [
                    d,
                    'OBJECT_ITEM_AFTER'
                ],
                overwrite: true
            },
            {
                id: 'ov',
                states: [
                    ':',
                    'CONDITIONAL'
                ]
            },
            {
                id: '_null',
                states: ['NULL']
            }
        ],
        MEMBER: [
            {
                id: '_jump',
                states: [
                    'MEMBER',
                    d
                ]
            },
            {
                id: '_op2',
                states: [
                    'MEMBER',
                    '.',
                    'VARIABLE_WITH_BLACK'
                ]
            },
            {
                id: '[]',
                states: [
                    'MEMBER',
                    '[',
                    'CONDITIONAL',
                    ']'
                ]
            },
            {
                id: 'f()',
                states: [
                    'MEMBER',
                    '(',
                    'FUNCARGS',
                    ')'
                ]
            },
            {
                id: '_jump',
                states: ['VALUE']
            }
        ],
        VARIABLE_WITH_BLACK: [
            {
                id: '_blank',
                states: [
                    d,
                    'VARIABLE_WITH_BLACK'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['VARIABLE']
            }
        ],
        FUNCARGS: [
            {
                id: '_blank',
                states: [
                    d,
                    'FUNCARGS'
                ],
                overwrite: true
            },
            {
                id: '_join',
                states: [
                    'FUNCARGS',
                    ',',
                    'CONDITIONAL'
                ]
            },
            {
                id: 'fa',
                states: ['CONDITIONAL']
            },
            {
                id: '_empty',
                states: ['NULL'],
                overwrite: true
            }
        ],
        LOGICAL: [
            {
                id: '_blank',
                states: [
                    d,
                    'LOGICAL'
                ],
                overwrite: true
            },
            {
                id: '_jump',
                states: ['LOGICAL_NOSPACE']
            }
        ],
        LOGICAL_NOSPACE: [
            {
                id: '!',
                states: [
                    '!',
                    'LOGICAL'
                ]
            },
            {
                id: '!',
                states: [
                    '+',
                    'LOGICAL'
                ]
            },
            {
                id: '!',
                states: [
                    '-',
                    'LOGICAL'
                ]
            },
            {
                id: '_jump',
                states: ['MEMBER']
            }
        ],
        MULTIPLY: [
            {
                id: '_op2',
                states: [
                    'MULTIPLY',
                    '*',
                    'LOGICAL'
                ]
            },
            {
                id: '_op2',
                states: [
                    'MULTIPLY',
                    '/',
                    'LOGICAL'
                ]
            },
            {
                id: '_op2',
                states: [
                    'MULTIPLY',
                    '%',
                    'LOGICAL'
                ]
            },
            {
                id: '_jump',
                states: ['LOGICAL']
            }
        ],
        ADDITION: [
            {
                id: '_op2',
                states: [
                    'ADDITION',
                    '+',
                    'MULTIPLY'
                ]
            },
            {
                id: '_op2',
                states: [
                    'ADDITION',
                    '-',
                    'MULTIPLY'
                ]
            },
            {
                id: '_jump',
                states: ['MULTIPLY']
            }
        ],
        COMPARISON: [
            {
                id: '<>',
                states: [
                    'COMPARISON',
                    '<',
                    'COMPARISON_AFTER'
                ]
            },
            {
                id: '<>',
                states: [
                    'COMPARISON',
                    '>',
                    'COMPARISON_AFTER'
                ]
            },
            {
                id: '_jump',
                states: ['ADDITION']
            }
        ],
        COMPARISON_AFTER: [
            {
                id: 'a=',
                states: [
                    '=',
                    'ADDITION'
                ]
            },
            {
                id: '_jump',
                states: ['ADDITION']
            }
        ],
        EQUALITY: [
            {
                id: '=!',
                states: [
                    'EQUALITY',
                    '=',
                    '=',
                    'EQUALITY_AFTER'
                ]
            },
            {
                id: '=!',
                states: [
                    'EQUALITY',
                    '!',
                    '=',
                    'EQUALITY_AFTER'
                ]
            },
            {
                id: '_jump',
                states: ['COMPARISON']
            }
        ],
        EQUALITY_AFTER: [
            {
                id: 'a=',
                states: [
                    '=',
                    'COMPARISON'
                ]
            },
            {
                id: '_jump',
                states: ['COMPARISON']
            }
        ],
        LOGICAL_AND: [
            {
                id: '&&||',
                states: [
                    'LOGICAL_AND',
                    '&',
                    '&',
                    'EQUALITY'
                ]
            },
            {
                id: '_jump',
                states: ['EQUALITY']
            }
        ],
        LOGICAL_OR: [
            {
                id: '&&||',
                states: [
                    'LOGICAL_OR',
                    '|',
                    '|',
                    'LOGICAL_AND'
                ]
            },
            {
                id: '_jump',
                states: ['LOGICAL_AND']
            }
        ],
        CONDITIONAL: [{
                id: '?',
                states: [
                    'LOGICAL_OR',
                    'CONDITIONAL_AFTER'
                ]
            }],
        CONDITIONAL_AFTER: [
            {
                id: '?:',
                states: [
                    '?',
                    'LOGICAL_OR',
                    ':',
                    'CONDITIONAL'
                ]
            },
            {
                id: '_null',
                states: ['NULL']
            }
        ],
        COMMA: [
            {
                id: ',',
                states: [
                    'COMMA',
                    ',',
                    'CONDITIONAL'
                ]
            },
            {
                id: '_jump',
                states: ['CONDITIONAL']
            }
        ]
    }, {
        _null: function (e) {
        },
        _empty: function (e) {
            return [];
        },
        _jump: function (e) {
            return e[0];
        },
        _concat: function (e) {
            return (e[0] || '') + (e[1] || '');
        },
        _blank: function (e) {
            return e[1];
        },
        'str^': function (e) {
            return e[1];
        },
        str$: function (e) {
            return '';
        },
        'str\\': function (e) {
            if (!STRING_UNESCAPE_MAP[e[1]])
                throw new Error('String escaping "\\' + e[1] + '" is currently not supported.');
            return STRING_UNESCAPE_MAP[e[1]] + e[2];
        },
        var: function (e) {
            return {
                p: 'var',
                v: e[0]
            };
        },
        str: function (e) {
            return {
                p: 'str',
                v: e[0]
            };
        },
        num: function (e) {
            return {
                p: 'num',
                v: Number(e[0])
            };
        },
        '()': function (e) {
            return {
                p: '()',
                v: e[1]
            };
        },
        al: function (e) {
            e[1].forEach(function (t, i) {
                if (null === t) {
                    i !== e[1].length - 1 ? e[1][i] = {
                        p: 'var',
                        v: 'undefined'
                    } : e[1].pop();
                };
            });
            return {
                p: 'arr',
                v: e[1]
            };
        },
        ai: function (e) {
            return [e[0]];
        },
        aie: function (e) {
            return [null];
        },
        _join: function (e) {
            return e[0].concat(e[2]);
        },
        ol: function (e) {
            var t = {};
            var i = e[1];
            for (var n = 0; n < i.length; n++)
                t[i[n].n] = i[n].v;
            return {
                p: 'obj',
                v: t
            };
        },
        onv: function (e) {
            return [{
                    n: e[0],
                    v: e[1] || {
                        p: 'var',
                        v: e[0]
                    }
                }];
        },
        ons: function (e) {
            return [{
                    n: e[0],
                    v: e[1] || {
                        p: 'str',
                        v: e[0]
                    }
                }];
        },
        onn: function (e) {
            return [{
                    n: e[0],
                    v: e[1] || {
                        p: 'num',
                        v: Number(e[0])
                    }
                }];
        },
        ov: function (e) {
            return e[1];
        },
        '!': function (e) {
            return {
                p: e[0] + 'n',
                v: e[1]
            };
        },
        _op1: function (e) {
            return {
                p: e[0],
                v: e[1]
            };
        },
        _op2: function (e) {
            return {
                p: e[1],
                v: [
                    e[0],
                    e[2]
                ]
            };
        },
        'f()': function (e) {
            return {
                p: 'f()',
                v: [
                    e[0],
                    e[2]
                ]
            };
        },
        fa: function (e) {
            return [e[0]];
        },
        '[]': function (e) {
            return {
                p: '[]',
                v: [
                    e[0],
                    e[2]
                ]
            };
        },
        '<>': function (e) {
            return 'a=' === e[2].p ? {
                p: e[1] + '=',
                v: [
                    e[0],
                    e[2].v
                ]
            } : {
                p: e[1],
                v: [
                    e[0],
                    e[2]
                ]
            };
        },
        'a=': function (e) {
            return {
                p: 'a=',
                v: e[1]
            };
        },
        '=!': function (e) {
            return 'a=' === e[3].p ? {
                p: e[1] + '==',
                v: [
                    e[0],
                    e[3].v
                ]
            } : {
                p: e[1] + '=',
                v: [
                    e[0],
                    e[3]
                ]
            };
        },
        '&&||': function (e) {
            return {
                p: e[1] + e[2],
                v: [
                    e[0],
                    e[3]
                ]
            };
        },
        '?': function (e) {
            return e[1] ? {
                p: '?:',
                v: [
                    e[0],
                    e[1][0],
                    e[1][1]
                ]
            } : e[0];
        },
        '?:': function (e) {
            return [
                e[1],
                e[3]
            ];
        },
        ',': function (e) {
            return {
                p: ',',
                v: [
                    e[0],
                    e[2]
                ]
            };
        }
    }));
    return l.parse('COMMA', e);
};
var c = exports.stringify = function (e, t) {
    var i = function (e) {
        var n = [];
        switch (e.p) {
        case 'var':
            return A.indexOf(e.v) >= 0 ? e.v : t.indexOf(e.v) >= 0 ? RUNTIME_NAMES.SUB_SCOPE + '.' + e.v : RUNTIME_NAMES.TOP_SCOPE + '.' + e.v;
        case 'str':
            return '"' + e.v.replace(STRING_ESCAPE_REGEXP, function (e) {
                return '\\' + STRING_ESCAPE_MAP[e];
            }) + '"';
        case 'num':
            return e.v;
        case 'arr':
            e.v.forEach(function (e) {
                n.push(i(e));
            });
            return '[' + n.join(',') + ']';
        case 'obj':
            for (var r in e.v) {
                var l = '"' + r.replace(STRING_ESCAPE_REGEXP, function (e) {
                    return '\\' + STRING_ESCAPE_MAP[e];
                }) + '"';
                n.push(l + ':' + i(e.v[r]));
            }
            return '{' + n.join(',') + '}';
        case '()':
            return '(' + i(e.v) + ')';
        case '.':
            return RUNTIME_NAMES.MEMBER + '(' + i(e.v[0]) + ',"' + e.v[1] + '")';
        case '[]':
            return RUNTIME_NAMES.MEMBER + '(' + i(e.v[0]) + ',' + i(e.v[1]) + ')';
        case 'f()':
            if ('var' !== e.v[0].p)
                throw new Error('The function name could only be a variable identifier.');
            e.v[1].forEach(function (e) {
                n.push(i(e));
            });
            return RUNTIME_NAMES.CALL + '(' + RUNTIME_NAMES.CALLER + ',' + RUNTIME_NAMES.METHODS + ',"' + e.v[0].v + '",[' + n.join(',') + '])';
        case '!n':
        case '+n':
        case '-n':
            return '(' + e.p[0] + i(e.v) + ')';
        case '+':
        case '-':
        case '*':
        case '/':
        case '%':
        case '>':
        case '<':
        case '>=':
        case '<=':
        case '==':
        case '!=':
        case '===':
        case '!==':
        case '&&':
        case '||':
        case ',':
            return i(e.v[0]) + e.p + i(e.v[1]);
        case '?:':
            return i(e.v[0]) + '?' + i(e.v[1]) + ':' + i(e.v[2]);
        default:
            throw new Error('The AST is illegal.');
        }
    };
    return '(' + i(e) + ')';
};
exports.lvalueStringify = function (e, t) {
    var i = [];
    for (var n = e; n;) {
        if ('var' === n.p)
            return A.indexOf(n.v) >= 0 ? null : t.indexOf(n.v) >= 0 ? '["' + n.v + '",' + i.join(',') + ']' : '[null,' + i.join(',') + ']';
        if ('.' !== n.p)
            if ('[]' !== n.p) {
                if ('()' !== n.p)
                    return null;
                n = n.v;
            } else
                i.unshift(c(n.v[1], t)), n = n.v[0];
        else
            i.unshift('"' + n.v[1] + '"'), n = n.v[0];
    }
    return null;
};
exports.searchBindingPaths = function (e, t) {
    var i = [];
    var n = '';
    var a = function (e, r, o) {
        if (o > 1)
            throw new Error('Dynamical binding paths are not supported.');
        switch (e.p) {
        case 'var':
            if (r) {
                if (A.indexOf(e.v) >= 0)
                    return;
                return t.indexOf(e.v) >= 0 ? ['"' + e.v + '"'] : ['null,"' + e.v + '"'];
            }
            if (o >= 1)
                throw new Error('Dynamical binding paths are not supported.');
            if (A.indexOf(e.v) >= 0)
                return;
            t.indexOf(e.v) >= 0 ? i.push('["' + e.v + '"]') : i.push('[null,"' + e.v + '"]');
            return;
        case 'str':
        case 'num':
        case 'arr':
        case 'obj':
            return;
        case '()':
            return a(e.v, r, o);
        case '.':
            if (r) {
                if (!(n = a(e.v[0], true, o)))
                    return;
                n.push('"' + e.v[1] + '"');
                return n;
            }
            if (!(n = a(e.v[0], true, o + 1)))
                return;
            n.push('"' + e.v[1] + '"');
            i.push('[' + n.join(',') + ']');
            return;
        case '[]':
            if (r) {
                if (!(n = a(e.v[0], true, o)))
                    return;
                a(e.v[1], true, o);
                n.push(c(e.v[1]));
                return n;
            }
            if (!(n = a(e.v[0], true, o + 1)))
                return;
            a(e.v[1], true, o + 1);
            n.push(c(e.v[1]));
            i.push('[' + n.join(',') + ']');
            return;
        case 'f()':
            a(e.v[0], false, o);
            e.v[1].forEach(function (e) {
                a(e, false, o);
            });
            return;
        case '!n':
        case '+n':
        case '-n':
            a(e.v, false, o);
            return;
        case '+':
        case '-':
        case '*':
        case '/':
        case '%':
        case '>':
        case '<':
        case '>=':
        case '<=':
        case '==':
        case '!=':
        case '===':
        case '!==':
        case '&&':
        case '||':
        case ',':
            a(e.v[0], false, o);
            a(e.v[1], false, o);
            return;
        case '?:':
            a(e.v[0], false, o);
            a(e.v[1], false, o);
            a(e.v[2], false, o);
            return;
        default:
            throw new Error('The AST is illegal.');
        }
    };
    e.forEach(function (e) {
        a(e, false, 0);
    });
    return '[' + i.join(',') + ']';
};
